package com.nulldev.util.io.util.test;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.concurrent.atomic.LongAdder;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.RandomUtil;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.io.IOUtils;
import com.nulldev.util.io.file.FileUtil;
import com.nulldev.util.io.util.TransferToBackport;
import com.nulldev.util.manage.legacy.Timer;

public class BufferPerformanceTest {

	static class ZeroDataInputStream extends InputStream {

		boolean __block;

		@Override
		public int read() throws IOException {
			if (this.__block)
				return -1;
			return 0;
		}

		@Override
		public int read(byte[] b) throws IOException {
			if (this.__block)
				return -1;
			Arrays.fill(b, (byte) 0);
			return b.length;
		}

		@Override
		public int read(byte[] b, int off, int len) throws IOException {
			if (this.__block)
				return -1;
			Arrays.fill(b, (byte) 0);
			return len;
		}

		public void block() {
			this.__block = true;
		}

		public void unblock() {
			this.__block = false;
		}

	}

	static class RandomDataInputStream extends InputStream {

		boolean __block;

		@Override
		public int read() throws IOException {
			if (this.__block)
				return -1;
			return RandomUtil.randInt(0, 255);
		}

		@Override
		public int read(byte[] b) throws IOException {
			if (this.__block)
				return -1;
			RandomUtil.bytes(b);
			return b.length;
		}

		@Override
		public int read(byte[] b, int off, int len) throws IOException {
			if (this.__block)
				return -1;
			RandomUtil.bytes(b);
			return len;
		}

		public void block() {
			this.__block = true;
		}

		public void unblock() {
			this.__block = false;
		}

	}

	static class CountingOutputStream extends OutputStream {

		final LongAdder la = new LongAdder();

		@Override
		public void write(int b) throws IOException {
			this.la.increment();
		}

		@Override
		public void write(byte[] b) throws IOException {
			this.la.add(b.length);
		}

		@Override
		public void write(byte[] b, int off, int len) throws IOException {
			if (len == -1)
				return;
			this.la.add(len);
		}

		public long getCount() {
			return this.la.sum();
		}

		public void reset() {
			this.la.reset();
		}

	}

	public static void main(String[] args) {
		final long timeLimit = 10000;
		final ZeroDataInputStream rand = new ZeroDataInputStream();
		final CountingOutputStream cnt = new CountingOutputStream();
		{
			final Timer t = new Timer(1000);
			t.start();
			ExecutorManager.globalAsyncSubmit(() -> {
				try {
					IOUtils.fastCopy(rand, cnt);
				} catch (IOException e) {
					JVM.errorln(e);
				}
				return null;
			});
			while (!t.hasReached(3000L)) {
				try {
					Thread.sleep(1000L);
				} catch (InterruptedException e) {
				}
			}
			rand.block();
			t.stop();
			JVM.println("[WARMUP] Written " + cnt.getCount() + " bytes (" + FileUtil.convertToStringRepresentation(cnt.getCount()) + ") in three seconds.");
			cnt.reset();
			rand.unblock();
		}
		{
			final Timer t = new Timer(1000);
			t.start();
			ExecutorManager.globalAsyncSubmit(() -> {
				try {
					IOUtils.fastCopy(rand, cnt);
				} catch (IOException e) {
					JVM.errorln(e);
				}
				return null;
			});
			while (!t.hasReached(timeLimit)) {
				try {
					Thread.sleep(1000L);
				} catch (InterruptedException e) {
				}
			}
			rand.block();
			t.stop();
			JVM.println("[FAST_COPY] Written " + cnt.getCount() + " bytes (" + FileUtil.convertToStringRepresentation(cnt.getCount()) + ") in ten seconds.");
			cnt.reset();
			rand.unblock();
		}
		{
			final Timer t = new Timer(1000);
			t.start();
			ExecutorManager.globalAsyncSubmit(() -> {
				try {
					IOUtils.copy(rand, cnt);
				} catch (IOException e) {
					JVM.errorln(e);
				}
				return null;
			});
			while (!t.hasReached(timeLimit)) {
				try {
					Thread.sleep(1000L);
				} catch (InterruptedException e) {
				}
			}
			rand.block();
			t.stop();
			JVM.println("[COPY] Written " + cnt.getCount() + " bytes (" + FileUtil.convertToStringRepresentation(cnt.getCount()) + ") in ten seconds.");
			cnt.reset();
			rand.unblock();
		}
		{
			final Timer t = new Timer(1000);
			t.start();
			ExecutorManager.globalAsyncSubmit(() -> {
				try {
					TransferToBackport.transferTo(rand, cnt);
				} catch (IOException e) {
					JVM.errorln(e);
				}
				return null;
			});
			while (!t.hasReached(timeLimit)) {
				try {
					Thread.sleep(1000L);
				} catch (InterruptedException e) {
				}
			}
			rand.block();
			t.stop();
			JVM.println("[JDK_BACKPORT] Written " + cnt.getCount() + " bytes (" + FileUtil.convertToStringRepresentation(cnt.getCount()) + ") in ten seconds.");
			cnt.reset();
			rand.unblock();
		}
	}

}
